# coding=utf-8
# coding=utf-8

import os
from datetime import datetime, timedelta

from sqlalchemy import Column, Integer, Float, String, ForeignKey, create_engine, Enum, DateTime, Date
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, backref

Base = declarative_base()
DB_FILE = '/var/lib/cadts/ccc/ccc.db'
DEFAULT_CONNECTION_URL = 'sqlite:///' + DB_FILE

__all__ = ['UserStatus', 'UserOvertime', 'User', 'UserStatusLog', 'OvertimeType', 'DbFactory']


class IdentifiedObject(Base):
    __abstract__ = True

    id = Column(Integer, primary_key=True, autoincrement=True)


class UserStatus(object):
    OK = u'在位'
    TRAVEL = u'出差'
    SICK_LEAVE = u'病假'
    LEAVE = u'事假'
    HOME = u'回家'
    STUDY_ABROAD = u'留学'
    STUDY_OUTSIDE = u'外校访学'
    STUDY_DEPART = u'单位学习'
    GRADUATED = u'已毕业'

    names = (OK, TRAVEL, SICK_LEAVE, HOME, LEAVE, STUDY_ABROAD, STUDY_DEPART, STUDY_OUTSIDE, GRADUATED)
    report_names = (OK, TRAVEL, HOME, SICK_LEAVE, LEAVE)
    normal_leave = (TRAVEL, HOME, SICK_LEAVE, LEAVE)


class OvertimeType(object):
    MORNING = u'早加班'
    NOON = u'午加班'
    NIGHT = u'晚加班'

    names = (MORNING, NOON, NIGHT)

    @staticmethod
    def value_of(s):
        if s in u'早':
            return OvertimeType.MORNING
        elif s in u'中午':
            return OvertimeType.NOON
        elif s in u'晚':
            return OvertimeType.NIGHT
        else:
            raise Exception(u"未知加班类型：{}".format(s))


class UserOvertime(IdentifiedObject):
    __tablename__ = 'user_overtime'

    type = Column(Enum(*OvertimeType.names), nullable=False)
    date = Column(Date(), nullable=False)
    book_time = Column(DateTime(), nullable=False)

    user_id = Column(Integer(), ForeignKey('user.id'), nullable=False)
    user = relationship('User', backref=backref('overtimes'))

    @staticmethod
    def new(user, overtime_type, day=None):
        now = datetime.now()
        return UserOvertime(
            user=user,
            type=overtime_type,
            date=now.date() if not day else day,
            book_time=now
        )


class User(IdentifiedObject):
    __tablename__ = 'user'

    name = Column(String(64), nullable=False)
    status = Column(Enum(*UserStatus.names), nullable=False)
    update_time = Column(DateTime(), nullable=False)
    gender = Column(Enum(u'男', u'女'), nullable=False)
    mobile = Column(String(16), nullable=False)
    # 作为加班地点
    avatar = Column(String(512))
    squad = Column(Integer(), nullable=False)
    weixin_id = Column(String(64))
    division = Column(Integer())
    section = Column(Integer())

    @staticmethod
    def new_user(name, gender, mobile, weixin_id):
        return User(
            name=name,
            status=UserStatus.OK,
            update_time=datetime.now(),
            gender=gender,
            mobile=mobile,
            avatar='',
            squad=0,
            weixin_id=weixin_id,
            division=0,
            section=0
        )

    @property
    def overtime_place(self):
        return self.avatar

    @overtime_place.setter
    def overtime_place(self, value):
        self.avatar = value


class UserStatusLog(IdentifiedObject):
    __tablename__ = 'user_status_log'

    status = Column(Enum(*UserStatus.names), nullable=False)
    update_time = Column(DateTime(), nullable=False)

    user_id = Column(Integer(), ForeignKey('user.id'), nullable=False)
    user = relationship('User', backref=backref('logs'))

    @staticmethod
    def new_log(user, status=None):
        return UserStatusLog(
            status=status if status else user.status,
            update_time=datetime.now(),
            user=user
        )


class WeekendOutType(object):
    Sat = u'周六'
    Sun = u'周日'
    SatSun = u'周六周日'

    names = (Sat, Sun, SatSun)


class WeekendOut(IdentifiedObject):
    __tablename__ = 'weekend_out'

    type = Column(Enum(*WeekendOutType.names), nullable=False)
    book_time = Column(DateTime(), nullable=False)
    weekend_date = Column(Date(), nullable=False)
    location = Column(String(64), nullable=False)

    user_id = Column(Integer(), ForeignKey('user.id'), nullable=False)
    user = relationship('User', backref=backref('weekend_outs'))

    @staticmethod
    def current_weekend_date():
        today = datetime.now().date()
        today = today + timedelta(days=6 - today.weekday())
        return today

    @staticmethod
    def new(user, type_, location):
        return WeekendOut(
            user=user,
            type=type_,
            location=location,
            book_time=datetime.now(),
            weekend_date=WeekendOut.current_weekend_date()
        )


class LocationRecord(IdentifiedObject):
    __tablename__ = 'holiday_safe'

    book_time = Column(DateTime(), nullable=False)
    book_date = Column(Date(), nullable=False)
    location = Column(String(32), nullable=False)
    latitude = Column(Float())
    longitude = Column(Float())

    user_id = Column(Integer(), ForeignKey('user.id'), nullable=False)
    user = relationship('User', backref=backref('locations'))

    @staticmethod
    def new(user, location=u'', latitude=0.0, longitude=0.0):
        now = datetime.now()
        return LocationRecord(
            book_time=now,
            book_date=now.date(),
            location=location,
            latitude=latitude,
            longitude=longitude,
            user=user
        )


class TaskRecord(IdentifiedObject):
    __tablename__ = 'task_record'

    task_name = Column(String(64), nullable=False)
    times = Column(Integer(), nullable=False)
    book_date = Column(Date(), nullable=False)

    user_id = Column(Integer(), ForeignKey('user.id'), nullable=False)
    user = relationship('User', backref=backref('tasks'))

    @staticmethod
    def new(user, task_name, times, book_date):
        return TaskRecord(
            user=user,
            task_name=task_name,
            times=times,
            book_date=book_date,
        )


class SessionWrapper:
    def __init__(self, session):
        self.session = session

    def __enter__(self):
        return self.session

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_val:
            self.session.rollback()
        else:
            self.session.commit()
        self.session.close()


class DbFactory:
    def __init__(self, connection_url=DEFAULT_CONNECTION_URL):
        self.connection_url = connection_url
        self.engine = create_engine(connection_url)
        self.make_session = sessionmaker(bind=self.engine)

    def init_schema(self):
        if self.connection_url.endswith(DB_FILE):
            dir_name = os.path.dirname(DB_FILE)
            if not os.path.isdir(dir_name):
                os.makedirs(dir_name)
        Base.metadata.create_all(self.engine)

    def create_connection(self):
        return self.engine.connect()

    def create_session(self, **kwargs):
        return SessionWrapper(self.make_session(**kwargs))
